﻿// ------------------------------------------------------------------------
// 项目名称：Canroc.Net 
// 版权归属：Canroc（https://gitee.com/canroc）
//
// 许可证信息
// Canroc.Net项目主要遵循 Apache 许可证（版本 2.0）进行分发和使用。许可证位于源代码树根目录中的 LICENSE-APACHE 文件。
//
// 使用条款：
// 1.使用本项目应遵守相关法律法规和许可证的要求。
// 2.不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动。
// 3.任何基于本项目二次开发而产生的一切法律纠纷和责任，我们不承担任何责任
//
// 免责声明
// 对于因使用本代码而产生的任何直接、间接、偶然、特殊或后果性损害，我们不承担任何责任。
//
// 其他重要信息
// Canroc.Net 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。
// ------------------------------------------------------------------------

using Org.BouncyCastle.Asn1.GM;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Engines;
using Org.BouncyCastle.Crypto.Generators;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Crypto.Signers;
using Org.BouncyCastle.Math;
using Org.BouncyCastle.Security;

namespace Canroc.Net.Core.Extension;

/// <summary>
///     SM2加密扩展
/// </summary>
public static class Sm2CryptoExtension
{
    /// <summary>
    ///     生成公钥和私钥
    /// </summary>
    public static (string privateKey, string publicKey) GenerateKeyHex()
    {
        ECKeyPairGenerator generator = new();
        generator.Init(new ECKeyGenerationParameters(new ECDomainParameters(GMNamedCurves.GetByName("SM2P256V1")),
            new SecureRandom()));
        var key = generator.GenerateKeyPair();

        var privateKey = Convert.ToBase64String(((ECPrivateKeyParameters)key.Private).D.ToByteArray());
        var publicKey = Convert.ToBase64String(((ECPublicKeyParameters)key.Public).Q.GetEncoded(false));

        return (privateKey, publicKey);
    }

    /// <summary>
    ///     解密数据
    /// </summary>
    /// <param name="data">加密数据</param>
    /// <param name="privateKey">私钥</param>
    /// <returns></returns>
    public static string Sm2Decrypt(this string data, string privateKey)
    {
        var key = Convert.FromBase64String(privateKey);
        ICipherParameters privateKeyParameters = new ECPrivateKeyParameters(new BigInteger(1, key),
            new ECDomainParameters(GMNamedCurves.GetByName("SM2P256V1")));
        SM2Engine sm2 = new();
        sm2.Init(false, privateKeyParameters);
        var bytes = Convert.FromBase64String(data);
        var result = sm2.ProcessBlock(bytes, 0, bytes.Length);

        return Encoding.UTF8.GetString(result);
    }

    /// <summary>
    ///     加密数据
    /// </summary>
    /// <param name="data">待加密数据</param>
    /// <param name="publicKey">公钥</param>
    /// <returns></returns>
    public static string Sm2Encrypt(this string data, string publicKey)
    {
        var key = Convert.FromBase64String(publicKey);
        var x9Ec = GMNamedCurves.GetByName("SM2P256V1");
        ICipherParameters publicKeyParameters =
            new ECPublicKeyParameters(x9Ec.Curve.DecodePoint(key), new ECDomainParameters(x9Ec));

        SM2Engine sm2 = new();
        sm2.Init(true, new ParametersWithRandom(publicKeyParameters));
        var dataBytes = Encoding.UTF8.GetBytes(data);
        var result = sm2.ProcessBlock(dataBytes, 0, dataBytes.Length);
        return Convert.ToBase64String(result);
    }

    /// <summary>
    ///     签名
    /// </summary>
    /// <param name="msg">信息</param>
    /// <param name="privateKey">私钥</param>
    /// <param name="id">id</param>
    /// <returns></returns>
    public static string Sm2Sign(this string msg, string privateKey, string? id = null)
    {
        var key = Convert.FromBase64String(privateKey);
        ICipherParameters privateKeyParameters = new ECPrivateKeyParameters(new BigInteger(1, key),
            new ECDomainParameters(GMNamedCurves.GetByName("SM2P256V1")));

        SM2Signer sm2 = new();
        ICipherParameters cp;
        if (id is not null)
        {
            cp = new ParametersWithID(new ParametersWithRandom(privateKeyParameters), Encoding.UTF8.GetBytes(id));
        }
        else
        {
            cp = new ParametersWithRandom(privateKeyParameters);
        }

        sm2.Init(true, cp);
        var msgBytes = Encoding.UTF8.GetBytes(msg);
        sm2.BlockUpdate(msgBytes, 0, msgBytes.Length);
        var result = sm2.GenerateSignature();

        return Convert.ToBase64String(result);
    }

    /// <summary>
    ///     验证签名
    /// </summary>
    /// <param name="msg">信息</param>
    /// <param name="publicKey">公钥</param>
    /// <param name="signature">签名</param>
    /// <param name="id">id</param>
    /// <returns></returns>
    public static bool Sm2VerifySign(this string msg, string publicKey, string signature, string? id = null)
    {
        var key = Convert.FromBase64String(publicKey);
        var x9Ec = GMNamedCurves.GetByName("SM2P256V1");
        ICipherParameters publicKeyParameters =
            new ECPublicKeyParameters(x9Ec.Curve.DecodePoint(key), new ECDomainParameters(x9Ec));

        SM2Signer sm2 = new();
        var cp = id is not null
            ? new ParametersWithID(publicKeyParameters, Encoding.UTF8.GetBytes(id))
            : publicKeyParameters;
        sm2.Init(false, cp);
        var msgBytes = Encoding.UTF8.GetBytes(msg);
        sm2.BlockUpdate(msgBytes, 0, msgBytes.Length);
        var signatureBytes = Convert.FromBase64String(signature);

        return sm2.VerifySignature(signatureBytes);
    }
}